using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using System.Data.Common;
using System.Data;
using Yogesh.ExcelXml;
using ICSharpCode.SharpZipLib.Zip;
using System.Text;
using System.Collections;
using System.Xml;
using System.Xml.XPath;

/// <summary>
/// Imports from an Excel XML spreadsheet
/// </summary>
public partial class Import : System.Web.UI.Page
{
    private Database db;

    private enum FileType {
        XML,
        Properties,
        Resx,
        Firefox
    }

    /// <summary>
    /// Clears errors from previous submissions
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void Page_Load(object sender, EventArgs e)
    {

        (viwFinished.FindControl("resultDiv") as Panel).Visible = false;
        (viwFinished.FindControl("errorDiv") as Panel).Visible = false;
        (viwFinished.FindControl("lstImportErrors") as BulletedList).Items.Clear();
        if(!Page.IsPostBack) {
            if(db == null || !db.Connected) {
                db = new Database();
                db.OpenConnection();
            }
            using(Database.Command cmd = db.CreateCommand()) {
                cmd.CommandText = "SELECT id, displayname FROM applications ORDER BY displayname ASC";
                Database.DataAdapter adapter = new Database.DataAdapter(cmd);
                DataSet ds = new DataSet();
                adapter.Fill(ds);
                radApplication.DataSource = ds;
                radApplication.DataValueField = "id";
                radApplication.DataTextField = "displayname";
                radApplication.DataBind();
            }
            if(radApplication.Items.Count > 0) {
                radApplication.SelectedIndex = 0;
                mltImport.ActiveViewIndex = 1;
            }
        }
    }
    
    /// <summary>
    /// Uploads the file and begins importing. If the file is a zip file, all files are extracted from it before continuing.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void btnUpload_Click(object sender, EventArgs e) {
        BulletedList errorList = viwFinished.FindControl("lstImportErrors") as BulletedList;
        (viwApp.FindControl("pnlInitialWarnings") as Panel).Visible = false;
        (viwApp.FindControl("lstInitialWarnings") as BulletedList).Items.Clear();
        if(uplXlsFile.HasFile) {
            try {
                string ext = Path.GetExtension(uplXlsFile.FileName).ToLowerInvariant().Substring(1); // the extension of the uploaded file (could be zip)
                string innerExtension = string.Empty; // the file within the zip
                Dictionary<string, string> files = new Dictionary<string, string>();
                StringBuilder fileBuffer = new StringBuilder();
                using(MemoryStream ms = new MemoryStream()) {
                    if(ext == "zip") {
                        ZipInputStream zipStream = new ZipInputStream(uplXlsFile.FileContent);
                        ZipEntry entry;
                        byte[] buffer = new byte[1024];
                        while((entry = zipStream.GetNextEntry()) != null) {
                            fileBuffer.Remove(0, fileBuffer.Length);
                            if(innerExtension == string.Empty) {
                                if(radFiletype.SelectedValue == "auto") {
                                    string detectedExt = Path.GetExtension(entry.Name);
                                    if(detectedExt.Length > 1) {
                                        innerExtension = detectedExt.ToLowerInvariant().Substring(1); // use the extension of the first file in the archive
                                    }
                                } else {
                                    innerExtension = radFiletype.SelectedValue;
                                }
                            }
                            StreamReader reader = new StreamReader(zipStream);
                            files.Add(entry.Name, reader.ReadToEnd());
                        }
                    } else {
                        using(StreamReader reader = new StreamReader(uplXlsFile.FileContent)) {
                            files.Add(uplXlsFile.FileName, reader.ReadToEnd().Replace("\r", ""));
                        }
                        if(radFiletype.SelectedValue == "auto") {
                            innerExtension = Path.GetExtension(uplXlsFile.FileName).ToLowerInvariant().Substring(1);
                        } else {
                            innerExtension = radFiletype.SelectedValue;
                        }
                    }
                }
                if(innerExtension.Length == 0) {
                    throw new Exception("Could not detect file type");
                }
                if(files.Count == 0) {
                    throw new Exception("No files found");
                }
                switch(innerExtension) {
                    case "xml":
                        foreach(KeyValuePair<string, string> file in files) {
                            ImportXML(file.Value);
                        }
                        break;
                    case "properties":
                        ImportProperties(files);
                        break;
                    case "resx":
                        ImportResx(files);
                        break;
                    case "dtd":

                        break;
                    default:
                        throw new Exception("Unsupported file type - " + innerExtension);
                }
            } catch(Exception ex) {
                (viwFinished.FindControl("errorDiv") as Panel).Visible = true;
                errorList.Items.Add(new ListItem(ex.Message));
                mltImport.ActiveViewIndex = 3;
            }
        } else {
            (viwFinished.FindControl("errorDiv") as Panel).Visible = true;
            errorList.Items.Add(new ListItem("No file specified"));
            mltImport.ActiveViewIndex = 3;
        }
    }

    /// <summary>
    /// Prompts for the application to which to add the uploaded bundles.
    /// </summary>
    /// <param name="bundles">The uploaded bundles.</param>
    private void PromptApplication(Dictionary<string, Dictionary<string, Dictionary<string, string>>> bundles) {
        mltImport.ActiveViewIndex = 2;
        (viwApp.FindControl("lblInitialResults") as Label).Text = string.Format("Uploaded <strong>{0}</strong> {1}.", bundles.Count, bundles.Count != 1 ? "bundles" : "bundle");
        Session["importfile"] = bundles;
    }

    /// <summary>
    /// Starts the import process.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void btnImport_Click(object sender, EventArgs e) {
        try {
            Dictionary<string, Dictionary<string, Dictionary<string, string>>> bundles = (Dictionary<string, Dictionary<string, Dictionary<string, string>>>)Session["importfile"];
            if(bundles == null) {
                throw new Exception("Error loading file");
            }
            ProcessBundles(bundles);
        } catch {
            mltImport.ActiveViewIndex = 3;
            (viwFinished.FindControl("lstImportErrors") as BulletedList).Items.Add("Error #407: Could not load file from cache.");
            (viwFinished.FindControl("errorDiv") as Panel).Visible = true;
        }
        Session["importfile"] = null;
    }

    /// <summary>
    /// Imports from a ResourceBlender XML file.
    /// </summary>
    /// <param name="file">The file.</param>
    private void ImportXML(string file) {
        mltImport.ActiveViewIndex = 3;
        BulletedList errorList = mltImport.FindControl("lstImportErrors") as BulletedList;
        try {
            if(db == null || !db.Connected) {
                db = new Database();
                db.OpenConnection();
            }
            ExcelXmlWorkbook wb;
            using(MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(file))) {
                wb = ExcelXmlWorkbook.Import(ms);
            }
            //todo: load meta from separate sheet

            List<string> languagesThisSheet = new List<string>();
            int startingColumn = 1;
            using(Database.Command cmdCheckExisting = db.CreateCommand())
            using(Database.Command cmdInsertTranslation = db.CreateCommand())
            using(Database.Command cmdInsertTranslationComment = db.CreateCommand())
            using(Database.Command cmdMaxRow = db.CreateCommand()) {
                cmdCheckExisting.CommandText = "SELECT COUNT(translationid) FROM translations WHERE translationid = @tID";
                cmdCheckExisting.AddParameterWithValue("@tID", "");
                switch(db.InstanceType) {
                    case Database.DatabaseType.MySQL:
                        cmdInsertTranslation.CommandText = "INSERT INTO translations (translationid, language, translation) VALUES (@tID, @lang, @trans) ON DUPLICATE KEY UPDATE translation = @trans";
                        cmdInsertTranslationComment.CommandText = "INSERT INTO translationcomments (translationid, comment) VALUES (@tID, @comm) ON DUPLICATE KEY UPDATE comment = @comm";
                        break;
                    case Database.DatabaseType.SQLServer:
                        cmdInsertTranslation.CommandText = "UPDATE translations SET translation = @trans WHERE translationid = @tID AND language = @lang; IF @@ROWCOUNT = 0 INSERT INTO translations (translationid, language, translation) VALUES (@tID, @lang, @trans)";
                        cmdInsertTranslationComment.CommandText = "UPDATE translationcomments SET comment = @comm WHERE translationid = @tID; IF @@ROWCOUNT = 0 INSERT INTO translationcomments (translationid, comment) VALUES (@tID, @comm)";
                        break;
                }
                cmdInsertTranslation.AddParameterWithValue("@tID", "");
                cmdInsertTranslation.AddParameterWithValue("@lang", "");
                cmdInsertTranslation.AddParameterWithValue("@trans", "");
                cmdInsertTranslationComment.AddParameterWithValue("@tID", "");
                cmdInsertTranslationComment.AddParameterWithValue("@comm", "");
                cmdMaxRow.CommandText = "SELECT MAX(translationid) FROM translations";
                int maxId = 0;
                int translationsUpdated = 0;
                int j, k;
                for(int i = 0; i < wb.SheetCount; i++) {
                    Worksheet sheet = wb[i];
                    languagesThisSheet.Clear();
                    for(int colIndex = startingColumn + 1; colIndex < sheet.ColumnCount; colIndex++) { //  first two cells are padding/labels
                        languagesThisSheet.Add(sheet[colIndex, 0].Value.ToString());
                    }
                    for(j = 1; j < sheet.RowCount; j++) {
                        try {
                            Row row = sheet[j];
                            int translationId = 0;
                            if(!int.TryParse(row[startingColumn].Value.ToString(), out translationId)) {
                                using(DbDataReader maxIdReader = cmdMaxRow.ExecuteReader(CommandBehavior.SingleRow)) {
                                    if(maxIdReader.HasRows) {
                                        maxIdReader.Read();
                                        int.TryParse(maxIdReader[0].ToString(), out maxId);
                                        translationId++;
                                    }
                                    maxIdReader.Close();
                                }
                            }
                            cmdCheckExisting.Parameters["@tID"].Value = translationId;
                            for(k = 0; k < row.CellCount - startingColumn - 1; k++) { // add each language to the translations table
                                string langAbbrev = languagesThisSheet[k];
                                string translation = row[k + startingColumn + 1].Value == null ? null : row[k + startingColumn + 1].Value.ToString();
                                if(translation != null && translation.Length > 0) {
                                    cmdInsertTranslation.Parameters["@tID"].Value = translationId;
                                    cmdInsertTranslation.Parameters["@lang"].Value = langAbbrev;
                                    cmdInsertTranslation.Parameters["@trans"].Value = translation.Replace("&gt;", ">").Replace("&lt;", "<");// HttpUtility.HtmlDecode(translation);
                                    cmdInsertTranslation.ExecuteNonQuery();
                                    translationsUpdated++;
                                }
                            }
                            string comment = row[startingColumn].Comment;
                            if(comment != null && comment.Length > 0) {
                                cmdInsertTranslationComment.Parameters["@tID"].Value = translationId;
                                cmdInsertTranslationComment.Parameters["@comm"].Value = comment.Replace("&gt;", ">").Replace("&lt;", "<"); ;
                                cmdInsertTranslationComment.ExecuteNonQuery();
                            }
                        } catch {
                            errorList.Items.Add(new ListItem("Error reading row " + j));
                        }
                        // TODO: fix deleting translation without deleting element
                        // TODO: add new translation by appending rows to spreadsheet (read max id etc.)
                    }
                }
                (mltImport.FindControl("resultDiv") as Panel).Visible = true;
                (mltImport.FindControl("lblSuccessInfo") as Label).Text = "Added " + translationsUpdated + " elements";
            }
        } catch(Exception ex) {
            errorList.Items.Add(new ListItem(ex.Message));
        }
        if(errorList.Items.Count > 0) {
            (viwFinished.FindControl("errorDiv") as Panel).Visible = true;
        }
    }

    /// <summary>
    /// Imports elements and translations from existing .properties bundles, then prompts for an application to which to import.
    /// </summary>
    /// <param name="files">The files.</param>
    private void ImportProperties(Dictionary<string, string> files) {
        BulletedList lstInitialWarnings = viwApp.FindControl("lstInitialWarnings") as BulletedList;
        Dictionary<string, Dictionary<string, Dictionary<string, string>>> bundles = new Dictionary<string, Dictionary<string, Dictionary<string, string>>>(); // <bundle, <elementname<language, translation>>>
        string bundleName, language, elementName, translation;
        int splitter, elementSplitter;
        Dictionary<string, Dictionary<string, string>> currentBundle;
        foreach(KeyValuePair<string, string> file in files) { // <filename, UTF8 contents>
            string fileNameNoExt = Path.GetFileNameWithoutExtension(file.Key);
            splitter = fileNameNoExt.IndexOf('_');
            if(splitter <= 0) {
                continue;
            }
            bundleName = fileNameNoExt.Substring(0, splitter);
            language = Utils.JavaCulture2DotNetCulture(fileNameNoExt.Substring(splitter + 1));
            if(bundles.ContainsKey(bundleName)) {
                currentBundle = bundles[bundleName];
            } else {
                currentBundle = new Dictionary<string, Dictionary<string, string>>();
                bundles.Add(bundleName, currentBundle);
            }
            string[] elements = file.Value.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
            foreach(string totalElement in elements) {
                try {
                    elementSplitter = totalElement.IndexOf('=');
                    elementName = totalElement.Substring(0, elementSplitter);
                    translation = totalElement.Substring(elementSplitter + 1);
                    if(currentBundle.ContainsKey(elementName)) {
                        currentBundle[elementName].Add(language, translation);
                    } else {
                        Dictionary<string, string> element = new Dictionary<string, string>();
                        element.Add(language, translation);
                        currentBundle.Add(elementName, element);
                    }
                } catch(Exception ex) {
                    lstInitialWarnings.Items.Add(string.Format("Failed to parse element '{0}': {1}", totalElement, ex.Message));
                }
            }
        }
        PromptApplication(bundles);
        if(lstInitialWarnings.Items.Count > 0) {
            (viwApp.FindControl("pnlInitialWarnings") as Panel).Visible = true;
        }
    }

    /// <summary>
    /// Imports from .NET managed resource (.resx) files.
    /// </summary>
    /// <param name="files">The files.</param>
    private void ImportResx(Dictionary<string, string> files) {
        BulletedList lstInitialWarnings = viwApp.FindControl("lstInitialWarnings") as BulletedList;
        Dictionary<string, Dictionary<string, Dictionary<string, string>>> bundles = new Dictionary<string, Dictionary<string, Dictionary<string, string>>>(); // <bundle, <elementname<language, translation>>>
        string bundleName, language, elementName, translation, dataType;
        XmlAttributeCollection attr;
        int splitter;
        Dictionary<string, Dictionary<string, string>> currentBundle;
        foreach(KeyValuePair<string, string> file in files) { // <filename, UTF8 contents>
            string fileNameNoExt = Path.GetFileNameWithoutExtension(file.Key);
            splitter = fileNameNoExt.IndexOf('.');
            if(splitter <= 0) {
                continue;
            }
            bundleName = fileNameNoExt.Substring(0, splitter);
            language = fileNameNoExt.Substring(splitter + 1).Replace('.', '-');
            switch(language) { // the GetCultures() method lists them as this, so if they're imported as Hans and Hant, they don't match up.
                case "zh-Hans":
                    language = "zh-CHS";
                    break;
                case "zh-Hant":
                    language = "zh-CHT";
                    break;
            }
            if(bundles.ContainsKey(bundleName)) {
                currentBundle = bundles[bundleName];
            } else {
                currentBundle = new Dictionary<string, Dictionary<string, string>>();
                bundles.Add(bundleName, currentBundle);
            }
            XmlDocument doc = new XmlDocument();
            try {
                doc.LoadXml(file.Value);
            } catch(Exception ex) {
                lstInitialWarnings.Items.Add("Invalid XML file: " + file.Key);
                System.Diagnostics.Debug.WriteLine(ex.Message);
                continue;
            }
            if(doc.ChildNodes.Count == 0) {
                lstInitialWarnings.Items.Add("Invalid resx file: " + file.Key);
                continue;
            }
            XmlNodeList nodes = doc.SelectNodes("/root/data[count(value)=1]");
            foreach(XmlNode node in nodes) {
                if(node.NodeType == XmlNodeType.Element) {
                    dataType = null;
                    attr = node.Attributes;
                    if(attr["type"] != null) {
                        dataType = attr["type"].Value;
                    } else if(attr["mimetype"] != null) {
                        dataType = attr["mimetype"].Value;
                    }
                    if(attr["name"] == null) {
                        lstInitialWarnings.Items.Add(string.Format("Invalid element in file {0}: name attribute missing", file.Key));
                        continue;
                    } else {
                        elementName = attr["name"].Value;
                    }
                    translation = string.Empty;
                    foreach(XmlNode child in node) {
                        if(child.NodeType == XmlNodeType.Element) {
                            if(child.Name == "value") {
                                translation = child.InnerText;
                            }
                        }
                    }
                    switch(dataType) {
                        case null: // not set, resource is a string
                            if(currentBundle.ContainsKey(elementName)) {
                                currentBundle[elementName].Add(language, translation);
                            } else {
                                Dictionary<string, string> element = new Dictionary<string, string>();
                                element.Add(language, translation);
                                currentBundle.Add(elementName, element);
                            }
                            break;
                    }
                }
            }
        }
        PromptApplication(bundles);
        if(lstInitialWarnings.Items.Count > 0) {
            (viwApp.FindControl("pnlInitialWarnings") as Panel).Visible = true;
        }
    }

    /// <summary>
    /// Inserts bundles, elements and translations into the database.
    /// </summary>
    /// <param name="bundles">The bundles.</param>
    private void ProcessBundles(Dictionary<string, Dictionary<string, Dictionary<string, string>>> bundles) {
        mltImport.ActiveViewIndex = 3;
        BulletedList errorList = mltImport.FindControl("lstImportErrors") as BulletedList;
        try {
            if(db == null || !db.Connected) {
                db = new Database();
                db.OpenConnection();
            }
            int numBundles = 0,
                numElements = 0,
                numTranslations = 0;
            using(Database.Command cmd = db.CreateCommand())
            using(Database.Command cmdInsertBundle = db.CreateCommand())
            using(Database.Command cmdCheckExisting = db.CreateCommand())
            using(Database.Command cmdInsertElement = db.CreateCommand())
            using(Database.Command cmdFindSimilar = db.CreateCommand())
            using(Database.Command cmdInsertTranslation = db.CreateCommand())
            using(Database.Command cmdMaxRow = db.CreateCommand()) {
                string appId = (viwApp.FindControl("radApplication") as RadioButtonList).SelectedValue,
                       defaultLanguage = string.Empty;
                string[] appLanguages = null;
                Hashtable appBundles = new Hashtable(); // <bundlename, bundleid>
                cmd.CommandText = "SELECT defaultlanguage, languages, bundles FROM applications WHERE id = @id";
                cmd.AddParameterWithValue("@id", appId);
                DbDataReader reader = cmd.ExecuteReader();
                while(reader.Read()) {
                    defaultLanguage = reader["defaultlanguage"].ToString();
                    appLanguages = reader["languages"].ToString().Trim().Split(',');
                }
                reader.Close();
                cmd.CommandText = "SELECT id, name FROM bundles WHERE application = @id";
                reader = cmd.ExecuteReader();
                while(reader.Read()) {
                    appBundles.Add(reader[1], reader[0]);
                }
                reader.Close();

                cmdInsertBundle.CommandText = "INSERT INTO bundles (id, name, application) VALUES (@id, @name, @application)";
                cmdInsertBundle.AddParameterWithValue("@id", "");
                cmdInsertBundle.AddParameterWithValue("@name", "");
                cmdInsertBundle.AddParameterWithValue("@application", appId);

                cmdCheckExisting.CommandText = "SELECT translationid FROM elements WHERE applicationid = @appId AND bundle = @bundle AND elementname = @element";
                cmdCheckExisting.AddParameterWithValue("@appId", appId);
                cmdCheckExisting.AddParameterWithValue("@bundle", "");
                cmdCheckExisting.AddParameterWithValue("@element", "");

                cmdInsertElement.CommandText = "INSERT INTO elements (applicationid, bundle, elementname, translationid) VALUES (@appId, @bundle, @element, @tId)";
                cmdInsertElement.AddParameterWithValue("@appId", appId);
                cmdInsertElement.AddParameterWithValue("@bundle", "");
                cmdInsertElement.AddParameterWithValue("@element", "");
                cmdInsertElement.AddParameterWithValue("@tId", "");

                cmdFindSimilar.CommandText = "SELECT translationid FROM translations WHERE language = @language AND translation = @translation";
                cmdFindSimilar.AddParameterWithValue("@language", defaultLanguage); // Only match if the default language for this app. is present in the uploaded resources
                cmdFindSimilar.AddParameterWithValue("@translation", "");

                switch(db.InstanceType) {
                    case Database.DatabaseType.MySQL:
                        cmdInsertTranslation.CommandText = "INSERT INTO translations (translationid, language, translation) VALUES (@tId, @language, @translation) ON DUPLICATE KEY UPDATE translation = @translation";
                        break;
                    case Database.DatabaseType.SQLServer:
                        cmdInsertTranslation.CommandText = "UPDATE translations SET translation = @translation WHERE translationid = @tId AND language = @language; IF @@ROWCOUNT = 0 INSERT INTO translations (translationid, language, translation) VALUES (@tId, @language, @translation)";
                        break;
                }
                cmdInsertTranslation.AddParameterWithValue("@tID", "");
                cmdInsertTranslation.AddParameterWithValue("@language", "");
                cmdInsertTranslation.AddParameterWithValue("@translation", "");

                cmdMaxRow.CommandText = "SELECT MAX(translationid) FROM translations";

                int translationId = 0;
                string bundleId;
                foreach(KeyValuePair<string, Dictionary<string, Dictionary<string, string>>> bundle in bundles) { // <bundle, <element, <language, translation>>>
                    numBundles++;
                    if(appBundles.ContainsKey(bundle.Key)) {
                        bundleId = appBundles[bundle.Key].ToString();
                    } else {
                        bundleId = Utils.GenID(true);
                        cmdInsertBundle.Parameters["@id"].Value = bundleId;
                        cmdInsertBundle.Parameters["@name"].Value = bundle.Key;
                        cmdInsertBundle.ExecuteNonQuery();
                        appBundles.Add(bundle.Key, bundleId);
                    }
                    cmdCheckExisting.Parameters["@bundle"].Value = bundleId;
                    cmdInsertElement.Parameters["@bundle"].Value = bundleId;
                    foreach(KeyValuePair<string, Dictionary<string, string>> element in bundle.Value) { // <element, <language, translation>>
                        numElements++;
                        cmdCheckExisting.Parameters["@element"].Value = element.Key;
                        reader = cmdCheckExisting.ExecuteReader();
                        if(reader.HasRows) { // the element already exists, overwrite existing languages with the ones from the bundle
                            reader.Read();
                            int.TryParse(reader[0].ToString(), out translationId);
                            reader.Close();
                        } else { // no matching elements, create one first
                            reader.Close();
                            cmdInsertElement.Parameters["@element"].Value = element.Key;

                            // check for other translations which might be the same, then append the translations in this bundle to them instead of creating a duplicate translation. 
                            bool readId = false;
                            if(element.Value.ContainsKey(defaultLanguage)) {
                                var d = element.Value[defaultLanguage];
                                cmdFindSimilar.Parameters["@translation"].Value = element.Value[defaultLanguage];
                                reader = cmdFindSimilar.ExecuteReader();
                                if(reader.HasRows) { // translation of current element matches one already in the database, use the existing translation ID
                                    reader.Read();
                                    translationId = reader.GetInt32(0);
                                    readId = true;
                                }
                                reader.Close();
                            }
                            if(!readId) { // no matching translations, find the next available translation ID
                                using(DbDataReader maxIdReader = cmdMaxRow.ExecuteReader(CommandBehavior.SingleRow)) {
                                    if(maxIdReader.HasRows) {
                                        maxIdReader.Read();
                                        int.TryParse(maxIdReader[0].ToString(), out translationId);
                                        translationId++;
                                    }
                                    maxIdReader.Close();
                                }
                            }
                            cmdInsertElement.Parameters["@tId"].Value = translationId;
                            cmdInsertElement.ExecuteNonQuery();
                        }
                        cmdInsertTranslation.Parameters["@tId"].Value = translationId;
                        foreach(KeyValuePair<string, string> translation in element.Value) { // <language, translation>>
                            numTranslations++;
                            cmdInsertTranslation.Parameters["@language"].Value = translation.Key;
                            cmdInsertTranslation.Parameters["@translation"].Value = translation.Value;
                            try {
                                cmdInsertTranslation.ExecuteNonQuery();
                            } catch(Exception ex) {
                                errorList.Items.Add(ex.Message);
                            }
                        }
                    }
                }
            }
            (viwFinished.FindControl("resultDiv") as Panel).Visible = true;
            (viwFinished.FindControl("lblSuccessInfo") as Label).Text = string.Format("Imported {0} translations from {1} bundles containing {2} elements.", numTranslations, numBundles, numElements);
        } catch(Exception ex) {
            errorList.Items.Add(new ListItem(ex.Message));
        }
        if(errorList.Items.Count > 0) {
            (viwFinished.FindControl("errorDiv") as Panel).Visible = true;
        }
    }
}
